/* LogDB.m - The log database.
 *
 * Copyright (C) 2008 Yuuichi Teranishi
 *
 * This file is derived from Stumbler, a program developed by iphone-wireless
 * project.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * published by the Free Software Foundation.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; If not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#import "LogDB.h"

NSObject *str2date(char *str) {
  int ret;
  sscanf(str, "%d", &ret);
  return [NSCalendarDate dateWithTimeIntervalSince1970: ret];
}

NSObject *str2int (char *str) {
  int ret;
  sscanf(str, "%d", &ret);
  return [NSNumber numberWithInt: ret];
}

NSObject *str2double(char *str) {
  float ret;
  sscanf(str, "%f", &ret);
  return [NSNumber numberWithFloat: ret];
}

static int date_callback(void *appData,
			 int argc,
			 char **argv,
			 char **azColName){
  //int daySec = 60 * 60 * 24;
  NSMutableArray *result = (NSMutableArray *)appData;
  if (argc == 1) {
    NSDate *dayDate = [NSDate dateWithNaturalLanguageString:
				[NSString stringWithCString: argv[0]
					  encoding: NSUTF8StringEncoding]];
    [result addObject:dayDate];
  }
  return 0;
}

static int query_callback(void *appData,
			  int argc,
			  char **argv,
			  char **azColName){
  int i;
  NSMutableArray *result = (NSMutableArray *)appData;
  NSMutableDictionary *entry = [[NSMutableDictionary alloc] init];

  for(i = 0; i < argc; i++){
    NSObject *val;
    if (strcmp(azColName[i], "lng") == 0) {
      val = argv[i]? str2double(argv[i]) : nil; 
    }
    else if (strcmp(azColName[i], "lat") == 0) {
      val = argv[i]? str2double(argv[i]) : nil;
    }
    else if (strcmp(azColName[i], "date") == 0) {
      val = argv[i]? str2date(argv[i]) : nil;
    }
    else if (strcmp(azColName[i], "precision") == 0) {
      val = argv[i]? str2int(argv[i]) : nil;
    }
    
    [entry setObject: val
	   forKey:[NSString stringWithCString: azColName[i]
			    encoding: NSUTF8StringEncoding]];
    //    printf("%s = %s\n", azColName[i], argv[i] ? argv[i]: "NULL");
  }
  [result addObject: entry];
  //  printf("\n");
  return 0;
}

void
sqlite3_interval2datestr(sqlite3_context *ctxt,
			 int argc,
			 sqlite3_value **argv) {
  time_t timer = sqlite3_value_int(argv[0]);
  struct tm *date = localtime (&timer);

  int year = date->tm_year + 1900;
  int mon = date->tm_mon + 1;
  int mday = date->tm_mday;
  char result[12]; /* XXXX-XX-XX */
  sprintf(result,"%4d/%02d/%02d 0:00", year, mon, mday);
  sqlite3_result_text(ctxt, result, strlen(result), SQLITE_TRANSIENT);
}


@implementation LogDB
- (LogDB *)initWithFile:(NSString *) file {
  _file = file;
  [self execSQL:@"create table MoveLog(id INTEGER PRIMARY KEY, \
lng DOUBLE PRECISION, \
lat DOUBLE PRECISION, \
date DOUBLE PRECISION, \
precision INTEGER)"];
  return self;
}

- (void) setAlertDelegate:(id)delegate {
  _delegate = delegate;
}

- (NSMutableArray *)execQuerySQL:(NSString *)query {
  sqlite3 *db;
  char *zErrMsg = 0;
  int rc;

  NSMutableArray *result = [[NSMutableArray alloc] init];
    
  rc = sqlite3_open([_file cStringUsingEncoding: NSUTF8StringEncoding], &db);
  if( rc ){
    [_delegate showAlert: [NSString stringWithFormat:
				    @"Can't open database: %d, %s\n",
				    rc, sqlite3_errmsg(db)]];
    sqlite3_close(db);
    return;
  }
  rc = sqlite3_exec(db, [query cStringUsingEncoding: NSUTF8StringEncoding],
		    query_callback, result, &zErrMsg);
  if( rc!=SQLITE_OK ){
    NSLog([NSString stringWithFormat:
		      @"SQL error: %d, %s\n",
		    rc, zErrMsg]);
  }
  sqlite3_close(db);
  return result;
}

// XX merge to execQuerySQL
- (void)execDateQuerySQL:(NSString *)query array:(NSMutableArray *) result{
  sqlite3 *db;
  char *zErrMsg = 0;
  int rc;

  rc = sqlite3_open([_file cStringUsingEncoding: NSUTF8StringEncoding], &db);

  if( rc ){
    [_delegate showAlert: [NSString stringWithFormat:
				    @"Can't open database: %d, %s\n",
				    rc, sqlite3_errmsg(db)]];
    sqlite3_close(db);
    return;
  }
  rc = sqlite3_create_function(db,"datestr", 1, SQLITE_ANY, NULL,
			       &sqlite3_interval2datestr, NULL, NULL);
  if( rc != SQLITE_OK ){
    NSLog([NSString stringWithFormat:
		      @"SQL error: %d, %s\n",
		    rc, zErrMsg]);
  }
  rc = sqlite3_exec(db, [query cStringUsingEncoding: NSUTF8StringEncoding],
		    date_callback, result, &zErrMsg);
  if( rc != SQLITE_OK ){
    NSLog([NSString stringWithFormat:
		      @"SQL error: %d, %s\n",
		    rc, zErrMsg]);
  }
  sqlite3_close(db);
}

- (int)execSQL:(NSString *)query {
  sqlite3 *db;
  char *zErrMsg = 0;
  int rc;

  rc = sqlite3_open([_file cStringUsingEncoding: NSUTF8StringEncoding], &db);
  if( rc ){
    [_delegate showAlert: [NSString stringWithFormat:
				    @"Can't open database: %d, %s\n",
				    rc, sqlite3_errmsg(db)]];
    sqlite3_close(db);
    return;
  }
  rc = sqlite3_exec(db, [query cStringUsingEncoding: NSUTF8StringEncoding],
		    NULL, 0, &zErrMsg);
  sqlite3_close(db);
  return rc;
}

- (NSMutableDictionary *)logEntryWithLng:(double)lng lat:(double)lat date:(double)date precision:(int)precision {
  return [NSMutableDictionary dictionaryWithObjectsAndKeys:
				[NSNumber numberWithDouble: lng], @"lng",
			      [NSNumber numberWithDouble: lat], @"lat",
			      [NSDate dateWithTimeIntervalSince1970: date], @"date",
			      [NSNumber numberWithInt: precision], @"precision", nil];
			      }

- (void)store:(NSMutableDictionary *) logEntry {
  [self execSQL:
	  [@"insert into MoveLog (lng, lat, date, precision) VALUES"
	    stringByAppendingFormat:
	      @"(%f, %f, %d, %d)",
	    [[logEntry objectForKey:@"lng"] doubleValue],
	    [[logEntry objectForKey:@"lat"] doubleValue],
	    ((int)[[logEntry objectForKey:@"date"] timeIntervalSince1970]),
	    [[logEntry objectForKey:@"precision"] intValue]]];
}

- (void) storeWithLng:(double)lng lat:(double)lat date:(double)date precision:(int)precision {
  [self store:
	  [self logEntryWithLng:lng lat:lat date:date precision:precision]];
}

// return an array of NSDictionary of log entry.
- (NSMutableArray *)searchByDate:(NSDate *)date {
  // NSLog(@"search by date=%@ -> 24h", date);
  return
    [self 
      execQuerySQL:
	[@"select lng, lat, date, precision from MoveLog where "
	  stringByAppendingFormat:
	    @"%d < date and date < %d",
	  ((int)[date timeIntervalSince1970]),
	  ((int)[date timeIntervalSince1970]) + 60 * 60 * 24]];
}

- (void)deleteDate:(NSDate *)date {
  if (date != nil) {
    [self 
      execQuerySQL:
	[@"delete from MoveLog where "
	  stringByAppendingFormat:
	    @"%d < date and date < %d",
	  ((int)[date timeIntervalSince1970]),
	  ((int)[date timeIntervalSince1970]) + 60 * 60 * 24]];
  }
}

- (NSString *)searchByDateForJSON:(NSDate *)date {
  
  NSMutableArray *logs = [self searchByDate: date];
  NSString *result = @"[";
  int i;
  for (i = 0; i < [logs count]; i++) {
    NSDictionary *log = [logs objectAtIndex: i];
    result = [result stringByAppendingFormat:
		       @"[%@, %@, %@]",
		     [log objectForKey: @"lng"],
		     [log objectForKey: @"lat"],
		     [log objectForKey: @"precision"]];
    if ([logs count] > (i + 1)) {
      result = [result stringByAppendingString:@", "];
    }
  }
  result = [result stringByAppendingString:@"]"];
  return result;
}

// return an array of NSDate.
- (void)addDatesToArray:(NSMutableArray *)array {
  [self
    execDateQuerySQL: @"select distinct datestr(date) from MoveLog order by date desc"
    array: array];
}
@end
